package com.itaming.lycheeframework.support.utils;

import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpSession;
import lombok.experimental.UtilityClass;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.util.*;
import java.util.function.Predicate;
import java.util.stream.Collectors;

/**
 * Web工具类
 *
 * @author A.Ming
 */
@UtilityClass
public class WebUtil {

    /**
     * 传递IP地址的头字段
     */
    private static final String[] IP_HEADER_NAMES = new String[]{
        "X-Forwarded-For",     // 最常见的代理字段
        "X-Real-IP",           // 一些代理可能只设置该字段
        "Proxy-Client-IP",     // Apache httpd、WebLogic
        "WL-Proxy-Client-IP",  // WebLogic
        "HTTP_CLIENT_IP",      // 一些代理服务器可能使用的字段
        "HTTP_X_FORWARDED_FOR" // 少见的代理字段
    };

    /**
     * 有效请求IP地址断言
     */
    private static final Predicate<String> VALID_IP_PREDICATE = (ip) -> StringUtil.isNotBlank(ip) && !StringPool.UNKNOWN.equalsIgnoreCase(ip);

    /**
     * 获取当前请求
     *
     * @return 请求
     */
    public static HttpServletRequest getRequest() {
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        if (requestAttributes instanceof ServletRequestAttributes servletRequestAttributes) {
            return servletRequestAttributes.getRequest();
        }
        return null;
    }

    /**
     * 获取请求URI
     *
     * @return URI字符串
     */
    public static String getRequestURI() {
        return getRequestURI(getRequest());
    }

    /**
     * 获取请求URI
     *
     * @param request 请求
     * @return URI字符串
     */
    public static String getRequestURI(HttpServletRequest request) {
        return Optional.ofNullable(request).map(HttpServletRequest::getRequestURI).orElse(null);
    }

    /**
     * 获取Header参数值
     *
     * @param name 参数名称
     * @return 参数值
     */
    public static String getHeader(String name) {
        return getHeader(getRequest(), name);
    }

    /**
     * 获取Header参数值
     *
     * @param request 请求
     * @param name    参数名称
     * @return 参数值
     */
    public static String getHeader(HttpServletRequest request, String name) {
        return Optional.ofNullable(request).map(r -> r.getHeader(name)).orElse(null);
    }

    /**
     * 获取Header参数值列表
     *
     * @param name 参数名称
     * @return 参数值列表
     */
    public static List<String> getHeaders(String name) {
        return getHeaders(getRequest(), name);
    }

    /**
     * 获取Header参数值列表
     *
     * @param request 请求
     * @param name    参数名称
     * @return 参数值列表
     */
    public static List<String> getHeaders(HttpServletRequest request, String name) {
        return request == null ? Collections.emptyList() : CollectionUtil.newArrayList(request.getHeaders(name));
    }

    /**
     * 获取Header参数名称列表
     *
     * @return 参数名称集合
     */
    public static List<String> getHeaderNames() {
        return getHeaderNames(getRequest());
    }

    /**
     * 获取Header参数名称列表
     *
     * @param request 请求
     * @return 参数名称集合
     */
    public static List<String> getHeaderNames(HttpServletRequest request) {
        return request == null ? Collections.emptyList() : CollectionUtil.newArrayList(request.getHeaderNames());
    }

    /**
     * 获取请求参数
     *
     * @param key 要获取的参数
     * @return 参数的值
     */
    public static String getParameter(String key) {
        return Optional.ofNullable(getRequest()).map(request -> request.getParameter(key)).orElse(null);
    }

    /**
     * 获取请求参数
     *
     * @param request 请求
     * @param key     要获取的参数
     * @return 参数的值
     */
    public static String getParameter(HttpServletRequest request, String key) {
        return Optional.ofNullable(request).map(r -> r.getParameter(key)).orElse(null);
    }

    /**
     * 获取请求参数字符串，如：{@code a=1&b=2}
     *
     * @return 参数字符串，如果参数为空返回空字符串
     */
    public static String getQueryString() {
        return getQueryString(getRequest());
    }

    /**
     * 获取请求参数字符串，如：{@code a=1&b=2}
     *
     * @return 参数字符串，如果参数为空返回空字符串
     */
    public static String getQueryString(HttpServletRequest request) {
        return Optional.ofNullable(request).map(HttpServletRequest::getQueryString).map(URLUtil::decode).orElse(StringPool.EMPTY);
    }

    /**
     * 获取请求参数字符串
     *
     * <p>
     * 所有参数根据参数名排序，使用 {@code &} 符号拼接
     *
     * @param ignoreKeys 要忽略的参数
     * @return 参数字符串，如果参数为空返回空字符串
     */
    public static String getSortedQueryString(String... ignoreKeys) {
        return getSortedQueryString(getRequest(), ignoreKeys);
    }

    /**
     * 获取请求参数字符串
     *
     * <p>
     * 所有参数根据参数名排序，使用 {@code &} 符号拼接
     *
     * @param request    请求
     * @param ignoreKeys 要忽略的参数
     * @return 参数字符串，如果参数为空返回空字符串
     */
    public static String getSortedQueryString(HttpServletRequest request, String... ignoreKeys) {
        if (request == null) {
            return StringPool.EMPTY;
        }

        Map<String, String[]> sortedParameterMap = new TreeMap<>(String::compareToIgnoreCase);

        Enumeration<String> enumeration = request.getParameterNames();
        while (enumeration.hasMoreElements()) {
            String name = enumeration.nextElement();
            if (!ArrayUtil.contains(ignoreKeys, name)) {
                String[] values = request.getParameterValues(name);
                sortedParameterMap.put(name, values);
            }
        }

        return sortedParameterMap.entrySet().stream()
            .flatMap(e -> Arrays.stream(e.getValue()).map(v -> String.format("%s=%s", e.getKey(), v)))
            .collect(Collectors.joining(StringPool.AMPERSAND));
    }

    /**
     * 获取SessionID
     *
     * @return SessionID
     */
    public static String getSessionId() {
        return getSessionId(getRequest());
    }

    /**
     * 获取 Session ID
     *
     * @return Session ID
     */
    public static String getSessionId(HttpServletRequest request) {
        return Optional.ofNullable(request)
            .map(HttpServletRequest::getSession)
            .map(HttpSession::getId)
            .orElse(null);
    }

    /**
     * 获取Session属性
     *
     * @param name 属性名
     * @return 属性值
     */
    public static Object getSessionAttribute(String name) {
        return getSessionAttribute(getRequest(), name);
    }

    /**
     * 获取Session属性
     *
     * @param request 请求
     * @param name    属性名
     * @return 属性值
     */
    public static Object getSessionAttribute(HttpServletRequest request, String name) {
        return Optional.ofNullable(request)
            .map(HttpServletRequest::getSession)
            .map(session -> session.getAttribute(name))
            .orElse(null);
    }

    /**
     * 获取Cookie
     *
     * @param name 指定名称
     * @return Cookie
     */
    public static Cookie getCookie(String name) {
        return getCookie(getRequest(), name);
    }

    /**
     * 获取Cookie
     *
     * @param request 请求
     * @param name    指定名称
     * @return Cookie
     */
    public static Cookie getCookie(HttpServletRequest request, String name) {
        if (request == null || StringUtil.isBlank(name)) {
            return null;
        }

        Cookie[] cookies = request.getCookies();
        if (cookies != null) {
            for (Cookie cookie : cookies) {
                if (name.equals(cookie.getName())) {
                    return cookie;
                }
            }
        }

        return null;
    }

    /**
     * 获取Cookie值
     *
     * @param name 指定名称
     * @return Cookie值
     */
    public static String getCookieValue(String name) {
        return getCookieValue(getRequest(), name);
    }

    /**
     * 获取Cookie值
     *
     * @param request 请求
     * @param name    指定名称
     * @return Cookie值
     */
    public static String getCookieValue(HttpServletRequest request, String name) {
        Cookie cookie = getCookie(request, name);
        return cookie != null ? cookie.getValue() : null;
    }

    /**
     * 获取客户端IP地址
     *
     * @return 客户端IP地址
     */
    public static String getClientIP() {
        return getClientIP(getRequest());
    }

    /**
     * 获取客户端IP地址
     *
     * @param request 请求
     * @return 客户端IP地址
     */
    public static String getClientIP(HttpServletRequest request) {
        if (request == null) {
            return null;
        }

        // 优先获取反向代理传递的IP地址
        String ip;
        for (String ipHeader : IP_HEADER_NAMES) {
            ip = request.getHeader(ipHeader);
            if (VALID_IP_PREDICATE.test(ip)) {
                ip = StringUtil.strip(ip.split(StringPool.COMMA)[0]);
                return !ip.isEmpty() ? ip : null;
            }
        }

        // 请求头中没有获取到IP地址，直接获取远程地址
        ip = request.getRemoteAddr();

        return VALID_IP_PREDICATE.test(ip) ? ip : null;
    }

}
